package cn.epsilon3.service.user.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.epsilon3.domain.e3.po.*;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.BCrypt;
import cn.hutool.crypto.digest.MD5;
import com.alibaba.fastjson2.JSONObject;
import cn.epsilon3.common.Result;
import cn.epsilon3.constant.Base;
import cn.epsilon3.constant.ResultEnum;
import cn.epsilon3.domain.e3.vo.E3CheckPasswordVO;
import cn.epsilon3.domain.e3.vo.E3ThirdUserInfoShareVO;
import cn.epsilon3.domain.e3.vo.FALoginVo;
import cn.epsilon3.domain.e3.vo.UserLoginVO;
import cn.epsilon3.exception.Epsilon3ReturnException;
import cn.epsilon3.utils.AssertUtils;
import cn.epsilon3.utils.SecurityUtils;
import cn.epsilon3.utils.WebUtils;
import cn.epsilon3.configBean.Initializing2FAConfig;
import cn.epsilon3.mybatis.mapper.E3ResourceMapper;
import cn.epsilon3.mybatis.service.E3RoleMapperService;
import cn.epsilon3.mybatis.service.E3UserAuthenticatorMapperService;
import cn.epsilon3.mybatis.service.E3UserMapperService;
import cn.epsilon3.mybatis.service.E3UserSaltMapperService;
import cn.epsilon3.service.user.E3UserService;
import cn.epsilon3.utils.AuthenticatorUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;

/**
 * <p>
 * 系统用户表 服务实现类
 * </p>
 *
 * @author epsilon3
 * @since 2023-05-19
 */
@Service
public class E3UserServiceImpl implements E3UserService {

    @Resource
    private E3UserMapperService userMapperService;

    @Resource
    private E3UserSaltMapperService userSaltMapperService;

    @Resource
    private E3UserAuthenticatorMapperService userAuthenticatorMapperService;

    @Resource
    private E3RoleMapperService roleMapperService;

    @Resource
    private E3ResourceMapper resourceMapper;

    @Resource
    private Initializing2FAConfig initializing2FAConfig;

    @Override
    public Result<Object> e3UserLogin(UserLoginVO userLoginVO) {

        String userAccount = userLoginVO.getUserAccount();
        E3UserPo e3UserPo = userMapperService.selectOneByUserCount(userAccount);
        if (ObjectUtil.isNull(e3UserPo)) {
            return new Result<>(ResultEnum.ERROR, "账户或密码错误");
        }
        String salt = userSaltMapperService.selectSaltByUserId(e3UserPo.getId());
        if (StrUtil.isBlank(salt)) {
            return new Result<>(ResultEnum.ERROR, "账户异常");
        }
        String digestHex = MD5.create().digestHex(userLoginVO.getUserPwd());
        String saltPwd = BCrypt.hashpw(digestHex, salt);
        if (!StrUtil.equals(saltPwd, e3UserPo.getUserPwd())) {
            return new Result<>(ResultEnum.ERROR, "账户或密码错误");
        }
        return verifyAccount(e3UserPo,new AccountInfo(),false);
    }

    @Override
    public Result<Object> e3UserFALogin(FALoginVo faLoginVo) {
        String code = faLoginVo.getCode();
        Role role = new Role();
        Fa fa = SecurityUtils.fa();
        Principal principal = SecurityUtils.principal();
        String secretKey = fa.getSecretKey();
        String generateCode = AuthenticatorUtils.generateCode(secretKey);
        if (!StrUtil.equals(generateCode, code)) {
            return new Result<>(ResultEnum.ERROR, "验证码错误");
        }
        E3UserPo e3UserPo = userMapperService.selectOneByUserId(principal.getId(), Base.INT0);
        E3RolePo e3RolePo = roleMapperService.selectOneByRoleId(e3UserPo.getRoleId());
        if (ObjectUtil.isNull(e3RolePo)) {
            return new Result<>(ResultEnum.ERROR, "账户异常");
        }
        role.setRoleName(e3RolePo.getRoleName());
        role.setRoleSign(e3RolePo.getRoleSign());
        role.setRoleId(e3RolePo.getId());
        StpUtil.login(principal.getId());
        AccountInfo sessionValue = (AccountInfo) WebUtils.getSessionValue(Base.E3_AUTH_INFO_SESSION);
        sessionValue.setRole(role);
        WebUtils.setSaSessionValue(Base.E3_AUTH_INFO_SESSION, sessionValue);
        return new Result<>(ResultEnum.SUCCESS_LOGIN);
    }

    @Override
    public Result<String> e3UserLoginOut() {
        String loginId = StpUtil.getLoginIdAsString();
        E3UserPo updateE3User = new E3UserPo();
        updateE3User.setId(loginId);
        updateE3User.setIsLogin(Base.INT1);
        userMapperService.updateOneByUserId(updateE3User);
        try {
            WebUtils.removeSaSessionValue(Base.E3_AUTH_INFO_SESSION);
            StpUtil.logout();
        } catch (Exception e) {
            return new Result<>(ResultEnum.SUCCESS, "用户已登出");
        }
        return new Result<>(ResultEnum.SUCCESS, "登出成功");
    }

    @Override
    public Result<String> e3CheckPassword(E3CheckPasswordVO e3CheckPasswordVO) {
        String password = e3CheckPasswordVO.getPassword();
        Principal principal = SecurityUtils.currentAccount().getPrincipal();
        String realPassword = principal.getPassword();
        String userId = principal.getId();
        String salt = userSaltMapperService.selectSaltByUserId(userId);
        String digestHex = MD5.create().digestHex(password);
        String saltPwd = BCrypt.hashpw(digestHex, salt);
        if (!StrUtil.equals(saltPwd, realPassword)) {
            return new Result<>(ResultEnum.ERROR, "密码错误");
        }
        return new Result<>(ResultEnum.SUCCESS);
    }

    @Override
    public Result<Object> thirdAuthLogin(E3ThirdUserInfoShareVO aliUserInfoShareVO) {
        // TODO 根据openId查找用户
        // 测试用户
        E3UserPo e3UserPo = userMapperService.selectOneByUserCount("admin");
        return verifyAccount(e3UserPo,new AccountInfo(),true);
    }

    @Override
    public Result<Object> executeLogin(AccountInfo accountInfo, E3UserPo e3UserPo, E3RolePo e3RolePo) {
        String now = DateUtil.now();
        String userId = e3UserPo.getId();
        E3UserPo updateE3User = new E3UserPo();
        updateE3User.setId(userId);
        updateE3User.setLastTime(now);
        updateE3User.setIsLogin(Base.INT2);
        userMapperService.updateOneByUserId(updateE3User);
        Role role = new Role();
        role.setRoleName(e3RolePo.getRoleName());
        role.setRoleSign(e3RolePo.getRoleSign());
        role.setRoleId(e3RolePo.getId());
        accountInfo.setRole(role);
        StpUtil.login(userId);
        setInfoSession(Base.INT2, accountInfo, e3UserPo);
        return new Result<>(ResultEnum.SUCCESS_LOGIN);
    }

    private Result<Object> verifyAccount(E3UserPo e3UserPo,
                                         AccountInfo accountInfo,
                                         Boolean isThirdLogin) {
        Integer isDisable = e3UserPo.getIsDisable();
        if (isDisable.equals(Base.INT1)) {
            throw new Epsilon3ReturnException("账户已被禁用");
        }
        E3RolePo e3RolePo = roleMapperService.selectOneByRoleId(e3UserPo.getRoleId());
        if (ObjectUtil.isNull(e3RolePo)) {
            throw new Epsilon3ReturnException("账户异常");
        }
        Fa fa = new Fa();
        E3UserAuthenticatorPo e3UserAuthenticatorPo = userAuthenticatorMapperService.selectOneByUserId(e3UserPo.getId());
        if (initializing2FAConfig.isEnable() &&
                ObjectUtil.isNotNull(e3UserAuthenticatorPo) &&
                Base.INT0.equals(e3UserAuthenticatorPo.getEffective())) {
            fa.setEnable(true);
            fa.setSecretKey(e3UserAuthenticatorPo.getSecretKey());
            accountInfo.setFa(fa);
            if (isThirdLogin) {
                return executeLogin(accountInfo, e3UserPo, e3RolePo);
            } else {
                setInfoSession(Base.INT1, accountInfo, e3UserPo);
                JSONObject jsonObject = new JSONObject(16);
                jsonObject.put("twoFA", true);
                return new Result<>(ResultEnum.SUCCESS, jsonObject);
            }
        } else {
            fa.setEnable(false);
            fa.setSecretKey(null);
            accountInfo.setFa(fa);
            return executeLogin(accountInfo, e3UserPo, e3RolePo);
        }
    }

    private void setInfoSession(Integer type,
                                AccountInfo accountInfo,
                                E3UserPo e3UserPo) {
        Principal principal = new Principal();
        principal.setUserEmail(e3UserPo.getUserEmail());
        principal.setUserName(e3UserPo.getUserName());
        principal.setId(e3UserPo.getId());
        principal.setPassword(e3UserPo.getUserPwd());
        String roleId = accountInfo.getRole().getRoleId();
        AssertUtils.isBlank(roleId,"账户异常");
        List<String> permissions = resourceMapper.queryRoleResourceOfF(roleId);
        principal.setPerms(permissions);
        accountInfo.setPrincipal(principal);
        if (Base.INT1.equals(type)) {
            WebUtils.setSessionValue(Base.E3_AUTH_INFO_SESSION, accountInfo);
        }
        if (Base.INT2.equals(type)) {
            WebUtils.setSaSessionValue(Base.E3_AUTH_INFO_SESSION, accountInfo);
        }
    }

    public static void main(String[] args) {
        String digestHex = MD5.create().digestHex("7dad8d7737afdf0f7590590f78acf67b");
        String saltPwd = BCrypt.hashpw(digestHex, "$2a$10$lZDhpzuLISdBfwZMY4dxPu");
        System.out.println(saltPwd);
    }
}
